package com.wzx.user.service.impl;

import com.alibaba.excel.util.DateUtils;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzx.common.exception.LeadNewsException;
import com.wzx.user.common.RedisPwdErrorNumCommon;
import com.wzx.user.constants.BusinessCon;
import com.wzx.user.constants.ZfbConstants;
import com.wzx.user.mapper.BindingMapper;
import com.wzx.user.mapper.NotesMapper;
import com.wzx.user.mapper.WalletMapper;
import com.wzx.user.pojo.Binding;
import com.wzx.user.pojo.Notes;
import com.wzx.user.pojo.Wallet;
import com.wzx.user.service.CommonService;
import com.wzx.user.service.WalletService;
import com.wzx.user.utils.AmountUtils;
import com.wzx.user.utils.DateUtil;
import com.wzx.user.vo.NotesVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author wzx
 * @version 1.0
 * @date 2023/3/1 - 22:37
 * @description 标题
 */
@Service
@Slf4j
@Transactional
public class WalletServiceImpl extends ServiceImpl<WalletMapper, Wallet> implements WalletService {

    public final String RSA2 = "RSA2";
    public final String CHARSET = "UTF-8";
    @Value("${zfbPay.url}")
    private String URL;

    @Value("${zfbPay.appId}")
    private String APPID;

    @Value("${zfbPay.appPrivateKey}")
    private String APP_PRIVATE_KEY;

    @Value("${zfbPay.alipayPublicKey}")
    private String ALIPAY_PUBLIC_KEY;

    @Value("${zfbPay.appCertPublicKey}")
    private String APP_CERT_PUBLIC_KEY;

    @Value("${zfbPay.alipayCertPublicKey}")
    private String ALIPAY_CERT_PUBLIC_KEY;

    @Value("${zfbPay.alipayRootCertPath}")
    private String ALIPAY_ROOT_CERT_PATH;

    @Autowired
    private WalletMapper walletMapper;

    @Autowired
    private NotesMapper notesMapper;

    @Autowired
    private BindingMapper bindingMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private CommonService commonService;

    @Autowired
    private RedisPwdErrorNumCommon redisPwdErrorNumCommon;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public double queryBalance(Integer userId) {
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", userId);
        Wallet wallet = walletMapper.selectOne(queryWrapper);
        if (wallet == null || wallet.getBalance() == null) {
            return 0L;
        }
        return AmountUtils.handlerAmountByDouble(wallet.getBalance());
    }

    @Override
    public void addBalance(NotesVo notesVo) {
        //1，查询用户余额
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", notesVo.getUserId());
        Wallet wallet = walletMapper.selectOne(queryWrapper);

        Integer addAmount = AmountUtils.handlerAmountByInteger(notesVo.getPrice());

        if (wallet == null) {
            //2，当数据为空 直接添加数据
            Wallet addWallet = new Wallet();
            addWallet.setBalance(addAmount);
            addWallet.setUid(notesVo.getUserId());
            walletMapper.insert(addWallet);
        } else if (wallet.getBalance() == null) {
            wallet.setBalance(addAmount);
            walletMapper.updateById(wallet);
        } else {
            //在原先基础上增加
            wallet.setBalance(wallet.getBalance() + addAmount);
            walletMapper.updateById(wallet);
        }
        //添加历史数据
        Notes notes = new Notes();
        notes.setType(notesVo.getType());
        notes.setPrice(addAmount);
        notes.setUserId(notesVo.getUserId());
        notes.setState(notesVo.getState());
        notes.setMethod(notesVo.getMethod());
        if (!StringUtils.isEmpty(notesVo.getAccountsObject())) {
            notes.setAccountsObject(notesVo.getAccountsObject());
        }
        if (!StringUtils.isEmpty(notesVo.getOrderId())) {
            notes.setOrderId(notesVo.getOrderId());
        }
        notesMapper.insert(notes);
    }

    @Override
    public String cashWithdrawal(NotesVo notesVo) throws LeadNewsException, AlipayApiException {
        //1，查询用户提现金额是否超过上限
        if(notesVo.getPrice() > ZfbConstants.Payment.MAX_WITHDRAWAL_AMOUNT){
            throw new LeadNewsException("单笔提现最多不能超过1000");
        }

        if(notesVo.getPrice() < ZfbConstants.Payment.MIN_WITHDRAWAL_AMOUNT){
            throw new LeadNewsException("单笔提现最低不能低于10");
        }

        //2，查询今日提现次数是否已达上限
        String redisKey = ZfbConstants.Payment.WITHDRAWAL_RECORD + notesVo.getUserId();
        Integer rechargeNum = (Integer) redisTemplate.opsForValue().get(redisKey);

        if(rechargeNum != null && rechargeNum >= ZfbConstants.Payment.MAX_NUM){
            throw new LeadNewsException("今日提现次数已达上线！");
        }


        //3，查询用户，密码错误次数
        redisPwdErrorNumCommon.getPwdErrorNum(notesVo.getUserId(), 1);

        String result = "提现成功";
        //4，查询用户是否绑定了提现账户
        QueryWrapper<Binding> queryBinding = new QueryWrapper<>();
        queryBinding.eq("userId", notesVo.getUserId());
        List<Binding> bindings = bindingMapper.selectList(queryBinding);
        if (bindings.isEmpty()) {
            throw new LeadNewsException("还未绑定提现账户，清先绑定");
        }

        //5，查询用户余额
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", notesVo.getUserId());
        Wallet wallet = walletMapper.selectOne(queryWrapper);
        if (wallet == null) {
            throw new LeadNewsException("还未进行实名认证，请先实名认证！");
        }
        Integer balance = wallet.getBalance();
        Integer cashPrice = AmountUtils.handlerAmountByInteger(notesVo.getPrice());

        //6，查询用户提现金额是否足够
        if (cashPrice > balance) {
            log.debug("用户{}当前可用余额是：{}(分)", notesVo.getUserId(), notesVo.getPrice());
            throw new LeadNewsException("余额不足！");
        }

        //7，低于10块钱无法体现
        if (balance <= 1000) {
            log.debug("用户{}当前可用余额是：{}(分)", notesVo.getUserId(), notesVo.getPrice());
            throw new LeadNewsException("余额过低，无法提现！");
        }

        //5，查询用户今日提现金额
        /*QueryWrapper<Notes> notesQueryWrapper = new QueryWrapper<>();
        queryWrapper.select("sum(price) as sumAll");
        queryWrapper.eq("userId", notesVo.getUserId());
        queryWrapper.eq("method", "2");
        Date start = DateUtil.strToDateLong(DateUtil.dateToStr(new Date(), Locale.CHINA) + " 00:00:00");
        Date end = DateUtil.strToDateLong(DateUtil.dateToStr(new Date(), Locale.CHINA) + " 23:59:59");
        queryWrapper.ge("createTime", start);
        queryWrapper.lt("createTime", end);
        Notes notesQueryPrice = notesMapper.selectOne(notesQueryWrapper);*/

        /*Integer allPrice = notesMapper.queryTodayPrice(notesVo.getUserId());
        if (allPrice != null && (allPrice >= 1000000 || (allPrice + cashPrice) > 1000000)) {
                throw new LeadNewsException("提现金额超过1000，请明天再来，或重新调整金额！");
        }*/

        Integer integer = notesMapper.queryTodaySum(notesVo.getUserId());
        if (integer > 3) {
            throw new LeadNewsException("今日已提现超三笔！");
        }

        //8，查询用户密码是否输入正确
        boolean decrypt = commonService.decrypt(notesVo.getUserId(), notesVo.getNewPwd(), wallet.getPassword());
        if (!decrypt) {
            log.debug("用户{}输入的错误密码是：{}", notesVo.getUserId(), notesVo.getNewPwd());
            redisPwdErrorNumCommon.setPwdErrorNum(notesVo.getUserId(), 1);
            throw new LeadNewsException("密码输入错误！");
        }
        redisPwdErrorNumCommon.closePwdErrorNum(notesVo.getUserId(), 1);

        //9，计算手续费
        double comPrice = AmountUtils.countCommission(notesVo.getPrice());
        log.info("用户{}提现需要扣除手续费：{}", notesVo.getUserId(), comPrice);
        Integer comPriceByInteger = AmountUtils.handlerAmountByInteger(comPrice);

        Integer finalPrice = cashPrice - comPriceByInteger;
        /*Integer deductPrice = cashPrice;
        if ((comPriceByInteger + cashPrice) > balance) {
            finalPrice = cashPrice - comPriceByInteger;
            deductPrice = cashPrice;
        } else {
            finalPrice = cashPrice;
            deductPrice = cashPrice + comPriceByInteger;
        }*/


        String state = "1";
        String noteType;
        try {
            //10，提现到账户
            CertAlipayRequest alipayConfig = new CertAlipayRequest();
            alipayConfig.setPrivateKey(APP_PRIVATE_KEY);
            alipayConfig.setServerUrl(URL);
            alipayConfig.setAppId(APPID);
            alipayConfig.setCharset(CHARSET);
            alipayConfig.setSignType(RSA2);
            alipayConfig.setEncryptor("");
            alipayConfig.setFormat("json");
            // 设置应用公钥证书路径
            alipayConfig.setCertPath(APP_CERT_PUBLIC_KEY);
            // 设置支付宝公钥证书路径
            alipayConfig.setAlipayPublicCertPath(ALIPAY_CERT_PUBLIC_KEY);
            // 设置支付宝根证书路径
            alipayConfig.setRootCertPath(ALIPAY_ROOT_CERT_PATH);

            AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
            AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
            AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
            String format = DateUtils.format(new Date(), DateUtils.DATE_FORMAT_14);
            model.setOutBizNo(format);
            model.setRemark("科兴科技有限公司转发");
            model.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
            model.setBizScene("DIRECT_TRANSFER");
            Participant payeeInfo = new Participant();

            //类型判断
            if (Objects.equals(bindings.get(0).getTypeId(), "2")) {
                payeeInfo.setIdentity(bindings.get(0).getZfbUserId());
                payeeInfo.setIdentityType("ALIPAY_USER_ID");
                payeeInfo.setName(bindings.get(0).getName());
            } else {
                payeeInfo.setIdentity(bindings.get(0).getPhone());
                payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
                payeeInfo.setName(bindings.get(0).getName());
            }


            model.setPayeeInfo(payeeInfo);
            model.setTransAmount(AmountUtils.handlerAmountByDouble(finalPrice) + "");
            model.setProductCode("TRANS_ACCOUNT_NO_PWD");
            model.setOrderTitle("科兴科技有限公司转发");
            request.setBizModel(model);
            AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);

            if (response.isSuccess()) {
                log.info("用户{}提现成功！", notesVo.getUserId());
                noteType = "支付宝提现：提现成功";

                //4，用户余额减少
                wallet.setBalance(wallet.getBalance() - cashPrice);
                walletMapper.updateById(wallet);

            } else {
                state = "2";
                log.error("提现失败：{}", response.getMsg());
                noteType = "支付宝提现：提现失败";
                result = "提现失败，提现失败金额已退回！";
            }
        } catch (AlipayApiException e) {
            state = "2";
            noteType = "支付宝提现：提现异常";
            log.error("提现错误！", e);
            result = "提现失败，提现失败金额已退回！";
        }

        //11，添加历史数据
        Notes notes = new Notes();
        notes.setType(noteType);
        notes.setPrice(cashPrice);
        notes.setUserId(notesVo.getUserId());
        notes.setState(state);
        notes.setMethod("2");
        notesMapper.insert(notes);

        //12，增加用户提现次数
        redisTemplate.opsForValue().increment(redisKey, 1);
        //这里设置12小时过期
        redisTemplate.expire(redisKey, 12, TimeUnit.HOURS);

        return result;
    }

    @Override
    public void updatePwd(NotesVo notesVo) throws LeadNewsException {
        //1，查询密码错误次数
        redisPwdErrorNumCommon.getPwdErrorNum(notesVo.getUserId(), 1);

        //查询 是否绑定过
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", notesVo.getUserId());
        Wallet wallet = walletMapper.selectOne(queryWrapper);
        if (wallet == null) {
            throw new LeadNewsException("为绑定！");
        }

        boolean decrypt = commonService.decrypt(notesVo.getUserId(), notesVo.getNewPwd(), wallet.getPassword());
        if (!decrypt) {
            redisPwdErrorNumCommon.setPwdErrorNum(notesVo.getUserId(), 1);
            throw new LeadNewsException("支付密码输入错误！");
        }
        redisPwdErrorNumCommon.closePwdErrorNum(notesVo.getUserId(), 1);

        //密码加密
        String encryption = commonService.encryption(notesVo.getUserId(), notesVo.getNewPwd());

        wallet.setPassword(encryption);
        walletMapper.updateById(wallet);

    }

    @Override
    public void forgetPwd(NotesVo notesVo) throws LeadNewsException {
        redisPwdErrorNumCommon.getYzmErrorNum(notesVo.getPhone());

        //获取redis验证码
        String code = stringRedisTemplate.opsForValue().get(BusinessCon.User.CODE_HEAD + notesVo.getPhone());
        if (code == null || !code.equals(notesVo.getCode())) {
            redisPwdErrorNumCommon.setYzmErrorNum(notesVo.getPhone());
            throw new LeadNewsException("验证码错误！");
        }
        redisPwdErrorNumCommon.closeYzmErrorNum(notesVo.getPhone());

        //查询 是否绑定过
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", notesVo.getUserId());
        Wallet wallet = walletMapper.selectOne(queryWrapper);
        if (wallet == null) {
            throw new LeadNewsException("为绑定！");
        }

        //密码加密
        String encryption = commonService.encryption(notesVo.getUserId(), notesVo.getNewPwd());

        wallet.setPassword(encryption);
        walletMapper.updateById(wallet);
    }

    @Override
    public Boolean judgePwd(NotesVo notesVo) throws LeadNewsException {
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", notesVo.getUserId());
        queryWrapper.eq("password", notesVo.getNewPwd());
        List<Wallet> wallets = walletMapper.selectList(queryWrapper);
        if (wallets.isEmpty()) {
            throw new LeadNewsException("密码输入错误！");
        }
        return true;
    }

}
